All files / web/src/app/api/classrooms/[classroomId]/enrollment-requests route.ts

0% Statements 0/139
0% Branches 0/1
0% Functions 0/1
0% Lines 0/139

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140                                                                                                                                                                                                                                                                                       
import { eq } from 'drizzle-orm'
import { NextResponse } from 'next/server'
import { db, schema } from '@/db'
import {
  createEnrollmentRequest,
  getLinkedParentIds,
  getPendingRequestsForClassroom,
  getRequestsAwaitingParentApproval,
  getTeacherClassroom,
  isParentOf,
} from '@/lib/classroom'
import { getSocketIO } from '@/lib/socket-io'
import { getUserId } from '@/lib/viewer'
import { withAuth } from '@/lib/auth/withAuth'

/**
 * GET /api/classrooms/[classroomId]/enrollment-requests
 * Get pending enrollment requests (teacher only)
 *
 * Returns:
 * - requests: Requests needing teacher approval (parent-initiated)
 * - awaitingParentApproval: Requests needing parent approval (teacher-initiated)
 */
export const GET = withAuth(async (_request, { params }) => {
  try {
    const { classroomId } = (await params) as { classroomId: string }
    const userId = await getUserId()

    // Verify user is the teacher of this classroom
    const classroom = await getTeacherClassroom(userId)
    if (!classroom || classroom.id !== classroomId) {
      return NextResponse.json({ error: 'Not authorized' }, { status: 403 })
    }

    // Fetch both types of pending requests in parallel
    const [requests, awaitingParentApproval] = await Promise.all([
      getPendingRequestsForClassroom(classroomId),
      getRequestsAwaitingParentApproval(classroomId),
    ])

    return NextResponse.json({ requests, awaitingParentApproval })
  } catch (error) {
    console.error('Failed to fetch enrollment requests:', error)
    return NextResponse.json({ error: 'Failed to fetch enrollment requests' }, { status: 500 })
  }
})

/**
 * POST /api/classrooms/[classroomId]/enrollment-requests
 * Create enrollment request (parent or teacher)
 *
 * Body: { playerId: string }
 * Returns: { request }
 */
export const POST = withAuth(async (req, { params }) => {
  try {
    const { classroomId } = (await params) as { classroomId: string }
    const userId = await getUserId()
    const body = await req.json()

    if (!body.playerId) {
      return NextResponse.json({ error: 'Missing playerId' }, { status: 400 })
    }

    // Determine role: is user the teacher or a parent?
    const classroom = await getTeacherClassroom(userId)
    const isTeacher = classroom?.id === classroomId
    const parentCheck = await isParentOf(userId, body.playerId)

    if (!isTeacher && !parentCheck) {
      return NextResponse.json(
        { error: 'Must be the classroom teacher or a parent of the student' },
        { status: 403 }
      )
    }

    const requestedByRole = isTeacher ? 'teacher' : 'parent'

    const request = await createEnrollmentRequest({
      classroomId,
      playerId: body.playerId,
      requestedBy: userId,
      requestedByRole,
    })

    // Get classroom and player info for the socket event
    const [classroomInfo] = await db
      .select({ name: schema.classrooms.name })
      .from(schema.classrooms)
      .where(eq(schema.classrooms.id, classroomId))
      .limit(1)

    const [playerInfo] = await db
      .select({ name: schema.players.name })
      .from(schema.players)
      .where(eq(schema.players.id, body.playerId))
      .limit(1)

    // Emit socket event to the classroom channel for real-time updates
    const io = await getSocketIO()
    if (io && classroomInfo && playerInfo) {
      try {
        const eventData = {
          request: {
            id: request.id,
            classroomId,
            classroomName: classroomInfo.name,
            playerId: body.playerId,
            playerName: playerInfo.name,
            requestedByRole,
          },
        }

        // Emit to classroom channel (for teacher's view)
        io.to(`classroom:${classroomId}`).emit('enrollment-request-created', eventData)
        console.log(
          `[Enrollment Request API] Emitted enrollment-request-created for classroom ${classroomId}`
        )

        // If teacher-initiated, also emit to parent's user channel
        // so they see the new pending approval in real-time
        if (requestedByRole === 'teacher') {
          const parentIds = await getLinkedParentIds(body.playerId)
          for (const parentId of parentIds) {
            io.to(`user:${parentId}`).emit('enrollment-request-created', eventData)
            console.log(`[Enrollment Request API] Notified parent ${parentId} of new request`)
          }
        }
      } catch (socketError) {
        console.error('[Enrollment Request API] Failed to broadcast request:', socketError)
      }
    }

    return NextResponse.json({ request }, { status: 201 })
  } catch (error) {
    console.error('Failed to create enrollment request:', error)
    return NextResponse.json({ error: 'Failed to create enrollment request' }, { status: 500 })
  }
})